import { Grammar } from 'prismjs';

import { CompletionItem } from '@grafana/ui';

// When changing RATE_RANGES, check if Loki/LogQL ranges should be changed too
// @see public/app/plugins/datasource/loki/LanguageProvider.ts
export const RATE_RANGES: CompletionItem[] = [
  { label: '$__interval', sortValue: '$__interval' },
  { label: '$__rate_interval', sortValue: '$__rate_interval' },
  { label: '$__range', sortValue: '$__range' },
  { label: '1m', sortValue: '00:01:00' },
  { label: '5m', sortValue: '00:05:00' },
  { label: '10m', sortValue: '00:10:00' },
  { label: '30m', sortValue: '00:30:00' },
  { label: '1h', sortValue: '01:00:00' },
  { label: '1d', sortValue: '24:00:00' },
];

export const OPERATORS = ['by', 'group_left', 'group_right', 'ignoring', 'on', 'offset', 'without'];
export const LOGICAL_OPERATORS = ['or', 'and', 'unless'];

const TRIGONOMETRIC_FUNCTIONS: CompletionItem[] = [
  {
    label: 'acos',
    insertText: 'acos',
    detail: 'acos(v instant-vector)',
    documentation: '计算v中所有元素的反余弦',
  },
  {
    label: 'acosh',
    insertText: 'acosh',
    detail: 'acosh(v instant-vector)',
    documentation: '计算v中所有元素的反双曲余弦',
  },
  {
    label: 'asin',
    insertText: 'asin',
    detail: 'asin(v instant-vector)',
    documentation: '计算v中所有元素的反正弦',
  },
  {
    label: 'asinh',
    insertText: 'asinh',
    detail: 'asinh(v instant-vector)',
    documentation: '计算v中所有元素的反双曲正弦',
  },
  {
    label: 'atan',
    insertText: 'atan',
    detail: 'atan(v instant-vector)',
    documentation: '计算v中所有元素的反正切',
  },
  {
    label: 'atanh',
    insertText: 'atanh',
    detail: 'atanh(v instant-vector)',
    documentation: '计算v中所有元素的反双曲正切',
  },
  {
    label: 'cos',
    insertText: 'cos',
    detail: 'cos(v instant-vector)',
    documentation: '计算v中所有元素的余弦',
  },
  {
    label: 'cosh',
    insertText: 'cosh',
    detail: 'cosh(v instant-vector)',
    documentation: '计算v中所有元素的双曲余弦',
  },
  {
    label: 'sin',
    insertText: 'sin',
    detail: 'sin(v instant-vector)',
    documentation: '计算v中所有元素的正弦',
  },
  {
    label: 'sinh',
    insertText: 'sinh',
    detail: 'sinh(v instant-vector)',
    documentation: '计算v中所有元素的双曲正弦',
  },
  {
    label: 'tan',
    insertText: 'tan',
    detail: 'tan(v instant-vector)',
    documentation: '计算v中所有元素的切线',
  },
  {
    label: 'tanh',
    insertText: 'tanh',
    detail: 'tanh(v instant-vector)',
    documentation: '计算v中所有元素的双曲正切',
  },
];

const AGGREGATION_OPERATORS: CompletionItem[] = [
  {
    label: 'sum',
    insertText: 'sum',
    documentation: '计算尺寸总和',
  },
  {
    label: 'min',
    insertText: 'min',
    documentation: '在尺寸上选择最小值',
  },
  {
    label: 'max',
    insertText: 'max',
    documentation: '选择尺寸上的最大值',
  },
  {
    label: 'avg',
    insertText: 'avg',
    documentation: '计算尺寸的平均值',
  },
  {
    label: 'group',
    insertText: 'group',
    documentation: '结果向量中的所有值均为1',
  },
  {
    label: 'stddev',
    insertText: 'stddev',
    documentation: '计算尺寸上的总体标准偏差',
  },
  {
    label: 'stdvar',
    insertText: 'stdvar',
    documentation: '计算维度上的总体标准方差',
  },
  {
    label: 'count',
    insertText: 'count',
    documentation: '计算矢量中的元素数',
  },
  {
    label: 'count_values',
    insertText: 'count_values',
    documentation: '计算具有相同值的元素数',
  },
  {
    label: 'bottomk',
    insertText: 'bottomk',
    documentation: '按样本值排列的最小k个元素',
  },
  {
    label: 'topk',
    insertText: 'topk',
    documentation: '按样本值排列的最大k个元素',
  },
  {
    label: 'quantile',
    insertText: 'quantile',
    documentation: '计算 φ-quantile (0 ≤ φ ≤ 1) 尺寸过大',
  },
];

export const FUNCTIONS = [
  ...AGGREGATION_OPERATORS,
  ...TRIGONOMETRIC_FUNCTIONS,
  {
    insertText: 'abs',
    label: 'abs',
    detail: 'abs(v instant-vector)',
    documentation: '返回将所有采样值转换为其绝对值的输入向量.',
  },
  {
    insertText: 'absent',
    label: 'absent',
    detail: 'absent(v instant-vector)',
    documentation:
      '如果传递给它的向量有任何元素，则返回一个空向量；如果传递给他的向量没有任何元素，返回一个值为1的单元素向量。这对于在给定度量名称和标签组合不存在时间序列时发出警报非常有用.',
  },
  {
    insertText: 'absent_over_time',
    label: 'absent_over_time',
    detail: 'absent(v range-vector)',
    documentation:
      '如果传递给它的范围向量有任何元素，则返回一个空向量；如果传递给他的范围向量没有元素，则值为1的1元素向量.',
  },
  {
    insertText: 'ceil',
    label: 'ceil',
    detail: 'ceil(v instant-vector)',
    documentation: '将“v”中所有元素的采样值四舍五入到最接近的整数.',
  },
  {
    insertText: 'changes',
    label: 'changes',
    detail: 'changes(v range-vector)',
    documentation:
      '对于每个输入时间序列, `changes(v range-vector)` 将其值在提供的时间范围内更改的次数作为即时向量返回.',
  },
  {
    insertText: 'clamp',
    label: 'clamp',
    detail: 'clamp(v instant-vector, min scalar, max scalar)',
    documentation:
      '钳制“v”中所有元素的采样值，使其下限为“min”，上限为“max”`.',
  },
  {
    insertText: 'clamp_max',
    label: 'clamp_max',
    detail: 'clamp_max(v instant-vector, max scalar)',
    documentation: '钳制“v”中所有元素的采样值，使其上限为“max”`.',
  },
  {
    insertText: 'clamp_min',
    label: 'clamp_min',
    detail: 'clamp_min(v instant-vector, min scalar)',
    documentation: '钳制“v”中所有元素的采样值，使其下限为“min”`.',
  },
  {
    insertText: 'count_scalar',
    label: 'count_scalar',
    detail: 'count_scalar(v instant-vector)',
    documentation:
      '以标量形式返回时间序列向量中的元素数。这与“count（）”聚合运算符形成对比，后者总是返回一个向量（如果输入向量为空，则为空），并允许通过“by”子句按标签分组.',
  },
  {
    insertText: 'deg',
    label: 'deg',
    detail: 'deg(v instant-vector)',
    documentation: '将v中所有元素的弧度转换为度数',
  },
  {
    insertText: 'day_of_month',
    label: 'day_of_month',
    detail: 'day_of_month(v=vector(time()) instant-vector)',
    documentation: '返回以UTC表示的每个给定时间的月份日期。返回值为1到31.',
  },
  {
    insertText: 'day_of_week',
    label: 'day_of_week',
    detail: 'day_of_week(v=vector(time()) instant-vector)',
    documentation:
      '返回以UTC表示的每个给定时间的星期几。返回的值从0到6，其中0表示周日等.',
  },
  {
    insertText: 'days_in_month',
    label: 'days_in_month',
    detail: 'days_in_month(v=vector(time()) instant-vector)',
    documentation:
      '返回一个月中每个给定时间的天数（UTC）。返回值为28到31.',
  },
  {
    insertText: 'delta',
    label: 'delta',
    detail: 'delta(v range-vector)',
    documentation:
      '计算范围向量“v”中每个时间序列元素的第一个值和最后一个值之间的差，返回具有给定delta和等效标签的即时向量。delta被外推以覆盖范围向量选择器中指定的整个时间范围，因此即使样本值都是整数，也可以获得非整数结果.',
  },
  {
    insertText: 'deriv',
    label: 'deriv',
    detail: 'deriv(v range-vector)',
    documentation:
      '使用简单线性回归计算范围向量“v”中时间序列的每秒导数.',
  },
  {
    insertText: 'drop_common_labels',
    label: 'drop_common_labels',
    detail: 'drop_common_labels(instant-vector)',
    documentation: '删除输入向量中所有系列中具有相同名称和值的所有标签.',
  },
  {
    insertText: 'exp',
    label: 'exp',
    detail: 'exp(v instant-vector)',
    documentation:
      '计算中所有元素的指数函数 `v`.\nSpecial cases are:\n* `Exp(+Inf) = +Inf` \n* `Exp(NaN) = NaN`',
  },
  {
    insertText: 'floor',
    label: 'floor',
    detail: 'floor(v instant-vector)',
    documentation: '将“v”中所有元素的采样值向下舍入为最接近的整数.',
  },
  {
    insertText: 'histogram_quantile',
    label: 'histogram_quantile',
    detail: 'histogram_quantile(φ float, b instant-vector)',
    documentation:
      '根据直方图的桶“b”计算φ-分位数（0≤φ≤1）。“b”中的样本是每个桶中观测值的计数。每个样本必须有一个标签“le”，其中标签值表示桶的包含上限。（没有此类标签的样本将被忽略。）直方图度量类型自动提供带有“_bucket”后缀和适当标签的时间序列.',
  },
  {
    insertText: 'holt_winters',
    label: 'holt_winters',
    detail: 'holt_winters(v range-vector, sf scalar, tf scalar)',
    documentation:
      '根据“v”中的范围为时间序列生成平滑值。平滑因子“sf”越低，就越重视旧数据。趋势因子“tf”越高，所考虑的数据趋势就越多。“sf”和“tf”都必须介于0和1之间.',
  },
  {
    insertText: 'hour',
    label: 'hour',
    detail: 'hour(v=vector(time()) instant-vector)',
    documentation: '返回一天中每个给定时间的小时（UTC）。返回值为0到23.',
  },
  {
    insertText: 'idelta',
    label: 'idelta',
    detail: 'idelta(v range-vector)',
    documentation:
      '计算范围向量“v”中最后两个样本之间的差，返回具有给定delta和等效标签的即时向量.',
  },
  {
    insertText: 'increase',
    label: 'increase',
    detail: 'increase(v range-vector)',
    documentation:
      '计算范围向量中时间序列的增量。单调性的中断（例如由于目标重新启动而导致的计数器重置）会自动调整。根据范围向量选择器中的指定，将增量外推到覆盖整个时间范围，因此即使计数器仅以整数增量增加，也可以获得非整数结果.',
  },
  {
    insertText: 'irate',
    label: 'irate',
    detail: 'irate(v range-vector)',
    documentation:
      '计算范围向量中时间序列每秒的瞬时增长率。这是基于最后两个数据点。单调性的中断（例如由于目标重新启动而导致的计数器重置）会自动调整.',
  },
  {
    insertText: 'label_join',
    label: 'label_join',
    detail:
      'label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...)',
    documentation:
      '对于“v”中的每个时间序列，使用“分隔符”连接所有“src_labels”的所有值，并返回带有包含连接值的标签“dst_label”的时间序列。此函数中可以有任意数量的“src_labels”.',
  },
  {
    insertText: 'label_replace',
    label: 'label_replace',
    detail: 'label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string)',
    documentation:
      "对于“v”中的每个时间序列，“label_replacement（v instant vector，dst_label string，replacement string，src_label string，regex string）”将正则表达式“regex”与标签“src_label'”相匹配。如果匹配，则返回时间序列，并将标签“dst_label”替换为“replacement”的扩展`$1’替换为第一个匹配的子组，'$2’替换为第二个等等。如果正则表达式不匹配，则返回时间序列而不改变.",
  },
  {
    insertText: 'ln',
    label: 'ln',
    detail: 'ln(v instant-vector)',
    documentation:
      '计算中所有元素的自然对数 `v`.\nSpecial cases are:\n * `ln(+Inf) = +Inf`\n * `ln(0) = -Inf`\n * `ln(x < 0) = NaN`\n * `ln(NaN) = NaN`',
  },
  {
    insertText: 'log2',
    label: 'log2',
    detail: 'log2(v instant-vector)',
    documentation:
      '计算“v”中所有元素的二进制对数。特殊情况与\'ln中的情况相同`.',
  },
  {
    insertText: 'log10',
    label: 'log10',
    detail: 'log10(v instant-vector)',
    documentation:
      '计算“v”中所有元素的十进制对数。特殊情况与\'ln中的情况相同`.',
  },
  {
    insertText: 'minute',
    label: 'minute',
    detail: 'minute(v=vector(time()) instant-vector)',
    documentation:
      '返回以UTC为单位的每个给定时间的小时中的分钟。返回值为0到59.',
  },
  {
    insertText: 'month',
    label: 'month',
    detail: 'month(v=vector(time()) instant-vector)',
    documentation:
      '返回一年中每个给定时间的月份（UTC）。返回的值从1到12，其中1表示一月等.',
  },
  {
    insertText: 'pi',
    label: 'pi',
    detail: 'pi()',
    documentation: 'Returns pi',
  },
  {
    insertText: 'predict_linear',
    label: 'predict_linear',
    detail: 'predict_linear(v range-vector, t scalar)',
    documentation:
      '基于范围向量“v”，使用简单线性回归预测从现在起时间序列“t”秒的值.',
  },
  {
    insertText: 'rad',
    label: 'rad',
    detail: 'rad(v instant-vector)',
    documentation: '将v中所有元素的度数转换为弧度',
  },
  {
    insertText: 'rate',
    label: 'rate',
    detail: 'rate(v range-vector)',
    documentation:
      "计算范围向量中时间序列每秒的平均增长率。单调性的中断（例如由于目标重新启动而导致的计数器重置）会自动调整。此外，该计算外推到时间范围的末尾，考虑到遗漏的刮擦或刮擦周期与范围的时间段不完全对齐.",
  },
  {
    insertText: 'resets',
    label: 'resets',
    detail: 'resets(v range-vector)',
    documentation:
      '对于每个输入时间序列, `resets（v range vector）’将所提供的时间范围内的计数器复位次数作为瞬时向量返回。两个连续采样之间的值的任何减少都被解释为计数器重置.',
  },
  {
    insertText: 'round',
    label: 'round',
    detail: 'round(v instant-vector, to_nearest=1 scalar)',
    documentation:
      '将“v”中所有元素的采样值四舍五入到最接近的整数。平局通过四舍五入解决。可选的“to_nearest”参数允许指定样本值应舍入到的最近倍数。这个倍数也可能是一个分数.',
  },
  {
    insertText: 'scalar',
    label: 'scalar',
    detail: 'scalar(v instant-vector)',
    documentation:
      '给定单个元素输入向量，“标量（v瞬时向量）”将该单个元素的采样值作为标量返回。如果输入向量没有恰好一个元素，“scalar”将返回“NaN”`.',
  },
  {
    insertText: 'sgn',
    label: 'sgn',
    detail: 'sgn(v instant-vector)',
    documentation:
      '返回一个向量，其中所有采样值都转换为其符号，定义如下：如果v为正，则为1；如果v为负，则为-1；如果v等于零，则为0.',
  },
  {
    insertText: 'sort',
    label: 'sort',
    detail: 'sort(v instant-vector)',
    documentation: '返回按样本值升序排序的矢量元素.',
  },
  {
    insertText: 'sort_desc',
    label: 'sort_desc',
    detail: 'sort_desc(v instant-vector)',
    documentation: '返回按样本值降序排序的矢量元素.',
  },
  {
    insertText: 'sqrt',
    label: 'sqrt',
    detail: 'sqrt(v instant-vector)',
    documentation: '计算`v中所有元素的平方根`.',
  },
  {
    insertText: 'time',
    label: 'time',
    detail: 'time()',
    documentation:
      '返回自1970年1月1日UTC以来的秒数。请注意，这实际上并不返回当前时间，而是要计算表达式的时间.',
  },
  {
    insertText: 'timestamp',
    label: 'timestamp',
    detail: 'timestamp(v instant-vector)',
    documentation:
      '返回自1970年1月1日UTC以来给定向量的每个样本的时间戳（以秒为单位）.',
  },
  {
    insertText: 'vector',
    label: 'vector',
    detail: 'vector(s scalar)',
    documentation: '将标量“s”作为不带标签的向量返回.',
  },
  {
    insertText: 'year',
    label: 'year',
    detail: 'year(v=vector(time()) instant-vector)',
    documentation: '以UTC为单位返回每个给定时间的年份.',
  },
  {
    insertText: 'avg_over_time',
    label: 'avg_over_time',
    detail: 'avg_over_time(range-vector)',
    documentation: '指定间隔内所有点的平均值.',
  },
  {
    insertText: 'min_over_time',
    label: 'min_over_time',
    detail: 'min_over_time(range-vector)',
    documentation: '指定间隔内所有点的最小值.',
  },
  {
    insertText: 'max_over_time',
    label: 'max_over_time',
    detail: 'max_over_time(range-vector)',
    documentation: '指定间隔内所有点的最大值.',
  },
  {
    insertText: 'sum_over_time',
    label: 'sum_over_time',
    detail: 'sum_over_time(range-vector)',
    documentation: '指定间隔内所有值的总和.',
  },
  {
    insertText: 'count_over_time',
    label: 'count_over_time',
    detail: 'count_over_time(range-vector)',
    documentation: '指定间隔内所有值的计数.',
  },
  {
    insertText: 'quantile_over_time',
    label: 'quantile_over_time',
    detail: 'quantile_over_time(scalar, range-vector)',
    documentation: '指定区间内值的φ-分位数（0≤φ≤1）.',
  },
  {
    insertText: 'stddev_over_time',
    label: 'stddev_over_time',
    detail: 'stddev_over_time(range-vector)',
    documentation: '指定区间内数值的总体标准偏差.',
  },
  {
    insertText: 'stdvar_over_time',
    label: 'stdvar_over_time',
    detail: 'stdvar_over_time(range-vector)',
    documentation: '指定区间内数值的总体标准方差.',
  },
  {
    insertText: 'last_over_time',
    label: 'last_over_time',
    detail: 'last_over_time(range-vector)',
    documentation: '指定间隔中的最新点值.',
  },
];

export const PROM_KEYWORDS = FUNCTIONS.map((keyword) => keyword.label);

export const promqlGrammar: Grammar = {
  comment: {
    pattern: /#.*/,
  },
  'context-aggregation': {
    pattern: /((by|without)\s*)\([^)]*\)/, // by ()
    lookbehind: true,
    inside: {
      'label-key': {
        pattern: /[^(),\s][^,)]*[^),\s]*/,
        alias: 'attr-name',
      },
      punctuation: /[()]/,
    },
  },
  'context-labels': {
    pattern: /\{[^}]*(?=}?)/,
    greedy: true,
    inside: {
      comment: {
        pattern: /#.*/,
      },
      'label-key': {
        pattern: /[a-z_]\w*(?=\s*(=|!=|=~|!~))/,
        alias: 'attr-name',
        greedy: true,
      },
      'label-value': {
        pattern: /"(?:\\.|[^\\"])*"/,
        greedy: true,
        alias: 'attr-value',
      },
      punctuation: /[{]/,
    },
  },
  function: new RegExp(`\\b(?:${FUNCTIONS.map((f) => f.label).join('|')})(?=\\s*\\()`, 'i'),
  'context-range': [
    {
      pattern: /\[[^\]]*(?=])/, // [1m]
      inside: {
        'range-duration': {
          pattern: /\b\d+[smhdwy]\b/i,
          alias: 'number',
        },
      },
    },
    {
      pattern: /(offset\s+)\w+/, // offset 1m
      lookbehind: true,
      inside: {
        'range-duration': {
          pattern: /\b\d+[smhdwy]\b/i,
          alias: 'number',
        },
      },
    },
  ],
  idList: {
    pattern: /\d+(\|\d+)+/,
    alias: 'number',
  },
  number: /\b-?\d+((\.\d*)?([eE][+-]?\d+)?)?\b/,
  operator: new RegExp(`/[-+*/=%^~]|&&?|\\|?\\||!=?|<(?:=>?|<|>)?|>[>=]?|\\b(?:${OPERATORS.join('|')})\\b`, 'i'),
  punctuation: /[{};()`,.]/,
};

export default promqlGrammar;
